package com.by.cloud.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.by.cloud.common.BaseContext;
import com.by.cloud.common.JwtProperties;
import com.by.cloud.common.PageResult;
import com.by.cloud.constants.JwtClaimsConstant;
import com.by.cloud.enums.UserRoleEnum;
import com.by.cloud.enums.UserStatusEnum;
import com.by.cloud.exception.BusinessException;
import com.by.cloud.exception.ErrorCode;
import com.by.cloud.exception.ThrowUtils;
import com.by.cloud.mapper.UserMapper;
import com.by.cloud.model.dto.user.*;
import com.by.cloud.model.entity.User;
import com.by.cloud.model.vo.user.UserLoginVo;
import com.by.cloud.model.vo.user.UserVo;
import com.by.cloud.service.UserService;
import com.by.cloud.utils.FileManager;
import com.by.cloud.utils.JwtUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author lzh
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Value("${user.encrypt.salt}")
    private String salt;

    @Autowired
    private JwtProperties jwtProperties;

    @Resource
    private FileManager fileManager;

    @Override
    public long userRegister(UserRegisterRequest userRegisterRequest) {
        // 请求参数
        String userAccount = userRegisterRequest.getUserAccount();
        String userPassword = userRegisterRequest.getUserPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();

        // 校验参数
        ThrowUtils.throwIf(StringUtils.isAnyBlank(userAccount, userPassword, checkPassword), ErrorCode.PARAMS_ERROR, "参数为空!");
        ThrowUtils.throwIf(userAccount.length() < 4, ErrorCode.PARAMS_ERROR, "用户账号长度不能小于4位！");
        checkPassword(userPassword, checkPassword);

        // 判断账号是否存在
        User dbUser = this.lambdaQuery()
                .eq(User::getUserAccount, userAccount)
                .one();
        ThrowUtils.throwIf(dbUser != null, ErrorCode.PARAMS_ERROR, "账号已存在");

        // 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((userPassword + salt).getBytes());

        // 插入数据库
        User user = new User();
        user.setUserAccount(userAccount);
        user.setUserPassword(encryptPassword);
        user.setUserRole(UserRoleEnum.USER.getValue());
        boolean flag = this.save(user);
        ThrowUtils.throwIf(!flag, ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误！");

        return user.getUserId();
    }

    /**
     * 检测原始密码俩次输入是否合法
     *
     * @param userPassword  第一次输入密码
     * @param checkPassword 第二次确定输入的密码
     */
    private static void checkPassword(String userPassword, String checkPassword) {
        ThrowUtils.throwIf(userPassword.length() < 8, ErrorCode.PARAMS_ERROR, "用户密码长度不能小于8位！");
        ThrowUtils.throwIf(userPassword.matches(".*\\s.*"), ErrorCode.PARAMS_ERROR, "用户密码不能包含空格、制表符、换行符等非法输入！");
        ThrowUtils.throwIf(!userPassword.equals(checkPassword), ErrorCode.PARAMS_ERROR, "密码不一致！");
    }

    @Override
    public UserLoginVo login(UserLoginDto userLoginDto) {
        ThrowUtils.throwIf(userLoginDto == null, ErrorCode.PARAMS_ERROR);
        String userAccount = userLoginDto.getUserAccount();
        String userPassword = getEncryptPassword(userLoginDto.getUserPassword(), salt);
        User user = this.lambdaQuery()
                .eq(User::getUserAccount, userAccount)
                .one();
        ThrowUtils.throwIf(user == null, ErrorCode.PARAMS_ERROR, "账号或密码错误");
        ThrowUtils.throwIf(!userPassword.equals(user.getUserPassword())
                        || user.getUserStatus().equals(UserStatusEnum.FREEZE.getValue()),
                ErrorCode.FORBIDDEN_ERROR, "密码错误或账号冻结");
        //登录成功后才会进行到这一步
        Map<String, Object> claims = new HashMap<>(5);
        claims.put(JwtClaimsConstant.USER_ID, user.getUserId());
        String authToken = JwtUtil.createJwt(
                jwtProperties.getUserSecretKey(),
                jwtProperties.getUserTtl(),
                claims
        );
        //封装返回实体
        UserLoginVo loginVo = new UserLoginVo();
        loginVo.setUserId(user.getUserId());
        loginVo.setUserAccount(user.getUserAccount());
        loginVo.setUserName(user.getUserName());
        loginVo.setAuthToken(authToken);
        return loginVo;
    }

    @Override
    public UserVo getUserById(Long userId) {
        User user = this.getById(userId);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(user, userVo);
        return userVo;
    }

    @Override
    public PageResult<UserVo> pageUsers(UserPageRequest userPageRequest) {
        // 获取参数
        String userName = userPageRequest.getUserName();
        String userAccount = userPageRequest.getUserAccount();
        String userProfile = userPageRequest.getUserProfile();
        Integer userRole = userPageRequest.getUserRole();
        int current = Math.max(userPageRequest.getCurrent(), 1);
        int pageSize = Math.max(userPageRequest.getCurrent(), 10);

        // 构建查询条件
        IPage<User> page = new Page<>(current, pageSize);
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StrUtil.isNotBlank(userAccount), User::getUserAccount, userAccount);
        queryWrapper.like(StrUtil.isNotBlank(userName), User::getUserName, userName);
        queryWrapper.like(StrUtil.isNotBlank(userProfile), User::getUserProfile, userProfile);
        queryWrapper.eq(userRole != null, User::getUserRole, userRole);
        queryWrapper.orderByDesc(User::getCreateTime);

        // 查询
        this.page(page, queryWrapper);

        // 分页封装
        long total = page.getTotal();
        List<User> records = page.getRecords();
        if (total <= 0) {
            return PageResult.of(0L, Collections.emptyList());
        }

        // 数据脱敏
        List<UserVo> userVoList = records.stream()
                .map(user -> BeanUtil.copyProperties(user, UserVo.class))
                .toList();
        return PageResult.of(total, userVoList);
    }

    @Override
    public List<UserVo> getUserByIdList(List<Long> userIds) {
        List<User> userList = this.lambdaQuery()
                .in(User::getUserId, userIds)
                .list();
        if (CollUtil.isEmpty(userList)) {
            return Collections.emptyList();
        }
        //返回vo列表
        return userList.stream()
                .map(user -> {
                    UserVo userVo = new UserVo();
                    BeanUtil.copyProperties(user, userVo);
                    return userVo;
                }).toList();
    }

    @Override
    public boolean updateByUserInfo(UserUpdateDto userDto) {
        ThrowUtils.throwIf(userDto == null, ErrorCode.PARAMS_ERROR);

        //用户是否存在校验
        Long userId = BaseContext.getCurrentId();
        User user = getById(userId);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR);

        //用户更新信息校验并更新
        return checkAndUpdateUserInfo(userDto, userId);
    }

    /**
     * 用户更新信息校验并更新
     *
     * @param userDto 传入的用户更新信息
     * @param userId  要更改的用户id
     * @param <T>     泛型
     * @return 是否通过校验
     */
    public <T extends UserUpdateDto> boolean checkAndUpdateUserInfo(T userDto, Long userId) {
        if (StrUtil.isNotBlank(userDto.getUserAccount())) {
            //账户不为空且长度小于4
            ThrowUtils.throwIf(userDto.getUserAccount().length() < 4, ErrorCode.PARAMS_ERROR, "用户账号长度不能小于4位！");
        }

        if ((StrUtil.isBlank(userDto.getUserPassword()) && StrUtil.isNotBlank(userDto.getCheckPassword()))
                || (StrUtil.isNotBlank(userDto.getUserPassword()) && StrUtil.isBlank(userDto.getCheckPassword()))) {
            //如果某一个密码为空，另一个不为空 则抛出异常
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "俩次密码输入不完整!");
        }

        if (StrUtil.isNotBlank(userDto.getUserPassword()) && StrUtil.isNotBlank(userDto.getCheckPassword())) {
            //到达此处说明用户输入了密码，原始俩次输入密码是否重复或者长度不够或者不合法，则不允许更新
            checkPassword(userDto.getUserPassword(), userDto.getCheckPassword());
        }

        // 如果只传了用户ID，则不更新数据
        if (StringUtils.isAllBlank(userDto.getUserAccount(), userDto.getUserPassword(),
                userDto.getCheckPassword(), userDto.getUserProfile(), userDto.getUserAvatar(), userDto.getUserName())) {
            return true;
        }

        //根据传入信息更新数据
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getUserId, userId);
        wrapper.set(StrUtil.isNotBlank(userDto.getUserAccount()), User::getUserAccount, userDto.getUserAccount());
        wrapper.set(StrUtil.isNotBlank(userDto.getUserPassword()), User::getUserPassword, getEncryptPassword(userDto.getUserPassword(), salt));
        wrapper.set(StrUtil.isNotBlank(userDto.getUserName()), User::getUserName, userDto.getUserName());
        wrapper.set(StrUtil.isNotBlank(userDto.getUserAvatar()), User::getUserAvatar, userDto.getUserAvatar());
        wrapper.set(StrUtil.isNotBlank(userDto.getUserProfile()), User::getUserProfile, userDto.getUserProfile());
        return this.update(wrapper);
    }

    @Override
    public UserVo getCurrentUserInfo() {
        Long userId = BaseContext.getCurrentId();
        ThrowUtils.throwIf(userId == null, ErrorCode.NOT_LOGIN_ERROR);
        return getUserById(userId);
    }

    @Override
    public boolean freezeByUserId(List<Long> userIds, Integer status) {
        if (CollUtil.isEmpty(userIds)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        return lambdaUpdate()
                .in(User::getUserId, userIds)
                .eq(User::getUserRole, UserRoleEnum.USER.getValue())
                .set(User::getUserStatus, status)
                .update();
    }

    @Override
    public boolean updateByAdmin(UserUpdateAdminDto userDto) {
        ThrowUtils.throwIf(userDto == null, ErrorCode.PARAMS_ERROR);
        Long adminUserId = BaseContext.getCurrentId();
        Long userId = userDto.getUserId();
        User user = getById(userId);
        if (user == null || adminUserId == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在,无法操作");
        }
        return checkAndUpdateUserInfo(userDto, userId);
    }

    @Override
    public boolean removeUserById(Long userId) {
        // 获取当前登录用户ID
        Long currentId = BaseContext.getCurrentId();
        // 如果ID相同则不允许删除
        if (userId.equals(currentId)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "不能删除当前登录用户");
        }
        return this.removeById(userId);
    }

    @Override
    public String uploadAvatar(MultipartFile multipartFile) {
        // 获取登录用户ID
        UserVo userInfo = this.getCurrentUserInfo();
        Long userId = userInfo.getUserId();
        // 指定路径前缀
        String pathPrefix = String.format("avatar/%s", userId);
        // 上传图片
        return fileManager.uploadAvatar(multipartFile, pathPrefix);
    }

    @Override
    public boolean isAdmin(Long userId) {
        ThrowUtils.throwIf(userId == null, ErrorCode.PARAMS_ERROR);
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("user_role");
        queryWrapper.eq("user_id", userId);
        User user = getOne(queryWrapper);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR);
        return user.getUserRole().equals(UserRoleEnum.ADMIN.getValue());
    }

    /**
     * 给用户密码加密后的结果
     *
     * @param salt 盐值
     * @return 密钥
     */
    public String getEncryptPassword(String userPassword, String salt) {
        return DigestUtils.md5DigestAsHex((userPassword + salt).getBytes());
    }
}




